package timeseries

import (
	

	
	
	
	
	
	
	
	
)

// Option represents an option that can be used to configure a graph panel.
type Option func(timeseries *TimeSeries) error

// TooltipMode configures which series will be displayed in the tooltip.
type TooltipMode string

const (
	// SingleSeries will only display the hovered series.
	SingleSeries TooltipMode = "single"
	// AllSeries will display all series.
	AllSeries TooltipMode = "multi"
	// NoSeries will hide the tooltip completely.
	NoSeries TooltipMode = "none"
)

// StackMode configures mode of series stacking.
type StackMode string

const (
	// Unstacked will not stack series
	Unstacked StackMode = "none"
	// NormalStack will stack series as absolute numbers
	NormalStack StackMode = "normal"
	// PercentStack will stack series as percents
	PercentStack StackMode = "percent"
)

// LineInterpolationMode defines how Grafana interpolates series lines when drawn as lines.
type LineInterpolationMode string

const (
	// Points are joined by straight lines.
	Linear LineInterpolationMode = "linear"
	// Points are joined by curved lines resulting in smooth transitions between points.
	Smooth LineInterpolationMode = "smooth"
	// The line is displayed as steps between points. Points are rendered at the end of the step.
	StepBefore LineInterpolationMode = "stepBefore"
	// Line is displayed as steps between points. Points are rendered at the beginning of the step.
	StepAfter LineInterpolationMode = "stepAfter"
)

// BarAlignment defines how Grafana aligns bars.
type BarAlignment int

const (
	// The bar is drawn around the point. The point is placed in the center of the bar.
	AlignCenter BarAlignment = 0
	// The bar is drawn before the point. The point is placed on the trailing corner of the bar.
	AlignBefore BarAlignment = -1
	// The bar is drawn after the point. The point is placed on the leading corner of the bar.
	AlignAfter BarAlignment = 1
)

// GradientType defines the mode of the gradient fill.
type GradientType string

const (
	// No gradient fill.
	NoGradient GradientType = "none"
	// Transparency of the gradient is calculated based on the values on the y-axis.
	// Opacity of the fill is increasing with the values on the Y-axis.
	Opacity GradientType = "opacity"
	// Gradient color is generated based on the hue of the line color.
	Hue GradientType = "hue"
	// In this mode the whole bar will use a color gradient defined by the color scheme.
	Scheme GradientType = "scheme"
)

// LegendOption allows to configure a legend.
type LegendOption uint16

const (
	// Hide keeps the legend from being displayed.
	Hide LegendOption = iota
	// AsTable displays the legend as a table.
	AsTable
	// AsList displays the legend as a list.
	AsList
	// Bottom displays the legend below the graph.
	Bottom
	// ToTheRight displays the legend on the right side of the graph.
	ToTheRight

	// Min displays the smallest value of the series.
	Min
	// Max displays the largest value of the series.
	Max
	// Avg displays the average of the series.
	Avg

	// First displays the first value of the series.
	First
	// FirstNonNull displays the first non-null value of the series.
	FirstNonNull
	// Last displays the last value of the series.
	Last
	// LastNonNull displays the last non-null value of the series.
	LastNonNull

	// Total displays the sum of values in the series.
	Total
	// Count displays the number of value in the series.
	Count
	// Range displays the difference between the minimum and maximum values.
	Range
)

// TimeSeries represents a time series panel.
type TimeSeries struct {
	Builder *sdk.Panel
	Alert   *alert.Alert
}

// New creates a new time series panel.
func ( string,  ...Option) (*TimeSeries, error) {
	 := &TimeSeries{Builder: sdk.NewTimeseries()}
	.Builder.IsNew = false

	for ,  := range append(defaults(), ...) {
		if  := ();  != nil {
			return nil, 
		}
	}

	return , nil
}

func defaults() []Option {
	return []Option{
		Span(6),
		LineWidth(1),
		FillOpacity(25),
		PointSize(5),
		Tooltip(SingleSeries),
		Legend(Bottom, AsList),
		Lines(Linear),
		GradientMode(Opacity),
		Axis(
			axis.Placement(axis.Auto),
			axis.Scale(axis.Linear),
		),
	}
}

// Links adds links to be displayed on this panel.
func ( ...links.Link) Option {
	return func( *TimeSeries) error {
		.Builder.Links = make([]sdk.Link, 0, len())

		for ,  := range  {
			.Builder.Links = append(.Builder.Links, .Builder)
		}

		return nil
	}
}

// DataSource sets the data source to be used by the graph.
func ( string) Option {
	return func( *TimeSeries) error {
		.Builder.Datasource = &sdk.DatasourceRef{LegacyName: }

		return nil
	}
}

// Tooltip configures the tooltip content.
func ( TooltipMode) Option {
	return func( *TimeSeries) error {
		.Builder.TimeseriesPanel.Options.Tooltip.Mode = string()

		return nil
	}
}

// LineWidth defines the width of the line for a series (default 1, max 10, 0 is none).
func ( int) Option {
	return func( *TimeSeries) error {
		if  < 0 ||  > 10 {
			return fmt.Errorf("line width must be between 0 and 10: %w", errors.ErrInvalidArgument)
		}

		.Builder.TimeseriesPanel.FieldConfig.Defaults.Custom.LineWidth = 

		return nil
	}
}

// Stack defines if the series should be stacked and using which mode (default not stacked).
func ( StackMode) Option {
	return func( *TimeSeries) error {
		.Builder.TimeseriesPanel.FieldConfig.Defaults.Custom.Stacking.Mode = string()

		return nil
	}
}

// FillOpacity defines the opacity level of the series. The lower the value, the more transparent.
func ( int) Option {
	return func( *TimeSeries) error {
		if  < 0 ||  > 100 {
			return fmt.Errorf("fill opacity must be between 0 and 100: %w", errors.ErrInvalidArgument)
		}

		.Builder.TimeseriesPanel.FieldConfig.Defaults.Custom.FillOpacity = 

		return nil
	}
}

// PointSize adjusts the size of points.
func ( int) Option {
	return func( *TimeSeries) error {
		if  < 0 ||  > 40 {
			return fmt.Errorf("point size must be between 0 and 40: %w", errors.ErrInvalidArgument)
		}

		.Builder.TimeseriesPanel.FieldConfig.Defaults.Custom.PointSize = 

		return nil
	}
}

// Lines displays the series as lines, with a given interpolation strategy.
func ( LineInterpolationMode) Option {
	return func( *TimeSeries) error {
		.Builder.TimeseriesPanel.FieldConfig.Defaults.Custom.LineInterpolation = string()
		.Builder.TimeseriesPanel.FieldConfig.Defaults.Custom.DrawStyle = "line"
		.Builder.TimeseriesPanel.FieldConfig.Defaults.Custom.LineStyle = struct {
			 string `json:"fill"`
		}{
			: "solid",
		}

		return nil
	}
}

// Bars displays the series as bars, with a given alignment strategy.
func ( BarAlignment) Option {
	return func( *TimeSeries) error {
		.Builder.TimeseriesPanel.FieldConfig.Defaults.Custom.BarAlignment = int()
		.Builder.TimeseriesPanel.FieldConfig.Defaults.Custom.DrawStyle = "bars"

		return nil
	}
}

// Points displays the series as points.
func () Option {
	return func( *TimeSeries) error {
		.Builder.TimeseriesPanel.FieldConfig.Defaults.Custom.DrawStyle = "points"

		return nil
	}
}

// GradientMode sets the mode of the gradient fill.
func ( GradientType) Option {
	return func( *TimeSeries) error {
		.Builder.TimeseriesPanel.FieldConfig.Defaults.Custom.GradientMode = string()

		return nil
	}
}

// Axis configures the axis for this time series.
func ( ...axis.Option) Option {
	return func( *TimeSeries) error {
		,  := axis.New(&.Builder.TimeseriesPanel.FieldConfig, ...)

		return 
	}
}

// Thresholds configures the thresholds for this time series.
func ( ...threshold.Option) Option {
	return func( *TimeSeries) error {
		threshold.New(&.Builder.TimeseriesPanel.FieldConfig, ...)

		return nil
	}
}

// ColorScheme configures the color scheme.
func ( ...scheme.Option) Option {
	return func( *TimeSeries) error {
		scheme.New(&.Builder.TimeseriesPanel.FieldConfig, ...)

		return nil
	}
}

// Legend defines what should be shown in the legend.
func ( ...LegendOption) Option {
	return func( *TimeSeries) error {
		 := true
		 := sdk.TimeseriesLegendOptions{
			Show:        &,
			DisplayMode: "list",
			Placement:   "bottom",
			Calcs:       make([]string, 0),
		}

		for ,  := range  {
			switch  {
			case Hide:
				 := false
				.DisplayMode = "hidden"
				.Show = &
			case AsList:
				.DisplayMode = "list"
			case AsTable:
				.DisplayMode = "table"
			case ToTheRight:
				.Placement = "right"
			case Bottom:
				.Placement = "bottom"

			case First:
				.Calcs = append(.Calcs, "first")
			case FirstNonNull:
				.Calcs = append(.Calcs, "firstNotNull")
			case Last:
				.Calcs = append(.Calcs, "last")
			case LastNonNull:
				.Calcs = append(.Calcs, "lastNotNull")

			case Min:
				.Calcs = append(.Calcs, "min")
			case Max:
				.Calcs = append(.Calcs, "max")
			case Avg:
				.Calcs = append(.Calcs, "mean")

			case Count:
				.Calcs = append(.Calcs, "count")
			case Total:
				.Calcs = append(.Calcs, "sum")
			case Range:
				.Calcs = append(.Calcs, "range")
			default:
				return fmt.Errorf("unknown legend option: %w", errors.ErrInvalidArgument)
			}
		}

		.Builder.TimeseriesPanel.Options.Legend = 

		return nil
	}
}

// Span sets the width of the panel, in grid units. Should be a positive
// number between 1 and 12. Example: 6.
func ( float32) Option {
	return func( *TimeSeries) error {
		if  < 1 ||  > 12 {
			return fmt.Errorf("span must be between 1 and 12: %w", errors.ErrInvalidArgument)
		}

		.Builder.Span = 

		return nil
	}
}

// Height sets the height of the panel, in pixels. Example: "400px".
func ( string) Option {
	return func( *TimeSeries) error {
		.Builder.Height = &

		return nil
	}
}

// Description annotates the current visualization with a human-readable description.
func ( string) Option {
	return func( *TimeSeries) error {
		.Builder.Description = &

		return nil
	}
}

// Transparent makes the background transparent.
func () Option {
	return func( *TimeSeries) error {
		.Builder.Transparent = true

		return nil
	}
}

// Alert creates an alert for this graph.
func ( string,  ...alert.Option) Option {
	return func( *TimeSeries) error {
		.Alert = alert.New(.Builder.Title, append(, alert.Summary())...)
		.Alert.Builder.Name = .Builder.Title

		return nil
	}
}

// Repeat configures repeating a panel for a variable
func ( string) Option {
	return func( *TimeSeries) error {
		.Builder.Repeat = &

		return nil
	}
}

// RepeatDirection configures repeating vertical or horizontal
func ( sdk.RepeatDirection) Option {
	return func( *TimeSeries) error {
		.Builder.RepeatDirection = &

		return nil
	}
}

// FieldOverride allows overriding visualization options.
func ( fields.Matcher,  ...fields.OverrideOption) Option {
	return func( *TimeSeries) error {
		 := sdk.FieldConfigOverride{}

		(&)

		for ,  := range  {
			(&)
		}

		.Builder.TimeseriesPanel.FieldConfig.Overrides = append(.Builder.TimeseriesPanel.FieldConfig.Overrides, )

		return nil
	}
}